import re

class GenSqueeze:
        def __init__(self, fileinput, prefixLength, filecomp = None):
                self.encoded = []
                self.sequence = self.getSequence(fileinput)
                self.prefixLength = int(prefixLength)
                self.origLen = len(self.sequence)
                self.unproc = 0
                self.compagainst = None
                self.compother = False
                #check whether compressing against another sequence
                if filecomp != None:
                    self.compother = True
                    self.compagainst = self.getSequence(filecomp)
                    
        #append encoded data and advance the unprocessed index
        def update(self, matchStart, matchLength):
                coded = '/' + str(matchStart) + ',' + str(matchLength)
                if len(coded) >= matchLength:
                        self.encoded.append(self.sequence[self.unproc])
                        self.unproc += 1
                else:
                    self.encoded.append(coded)
                    self.unproc += matchLength
                    
        #find all exact matches of prefix in sequence compressing against
        def findMatches(self):
                matches = []
                eind = self.unproc + self.prefixLength
                end = self.unproc - 1
                if self.unproc == 0:
                    end = 0
                
                if self.compother:
                    match = self.compagainst.find(self.sequence[self.unproc:eind])
                    while match != -1:
                        matches.append(match)
                        match = self.compagainst.find(self.sequence[self.unproc:eind],match + 1)
                else:
                    match = self.sequence.find(self.sequence[self.unproc:eind],0,end)
                    while match != -1 and match < end:
                            matches.append(match)
                            match = self.sequence.find(self.sequence[self.unproc:eind], match+1,end)
                return matches
        
        #check whether initial match can be extended further while staying below the mismatch ratio
        def extendMatch(self, match, ratio = 0.1):
                length = self.prefixLength
                mismatches = 0
                seqmax = self.origLen - 1
               
                if self.compother:
                    compmax = len(self.compagainst)
                    while match + length < compmax and self.unproc + length < seqmax:
                        if float(mismatches)/length >= ratio:
                                return length - 1, mismatches - 1
                        if self.compagainst[match + length] != self.sequence[self.unproc + length]:
                                mismatches += 1
                        length += 1
                else:
                    while match + length < seqmax and self.unproc + length < seqmax:
                        if float(mismatches)/length >= ratio:
                            return length - 1, mismatches -1
                        if self.sequence[match+length] != self.sequence[self.unproc + length]:
                            mismatches += 1
                        length += 1
                       
                return length, mismatches
                    
        #find longest match based on list of exact prefix matches
        def findLongest(self, matches,mismatch):
                bestMatch = None
                bestLength = 0
                bestMismatches = self.origLen
                for match in matches:
                        length, mismatches = self.extendMatch(match,mismatch)
                        if length > bestLength:
                                bestMatch = match
                                bestLength = length
                                bestMismatches = mismatches
                        elif length == bestLength and mismatches < bestMismatches:
                                bestMatch = match
                                bestLength = length
                                bestMismatches = mismatches
                return [bestMatch, bestLength, bestMismatches]
        
        #strip all non-sequence data from input
        def getSequence(self, fileinput):
                pattern2 = re.compile('^\>.*$', re.MULTILINE)
                match = pattern2.sub('', fileinput)
                pattern = re.compile('[^actg]', re.IGNORECASE)
                match = pattern.sub('', match)
                match = match.upper()
                return match
        #compress sequence
        def compress(self,mmratio):
                mmratio = float(mmratio)
                while self.unproc < self.origLen:
                        matches = self.findMatches()
                        start, length, mismatches = self.findLongest(matches,mmratio)
                        self.update(start, length)
                
        

